/*
 * SPDX-License-Identifier: BSD-3-Clause
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved.
 * Description: udk common define
 * Author: -
 * Create: 2021.4.19
 */
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <errno.h>

#include "securec.h"
#include "udk_common.h"
#include "udk_ring.h"
#include "udk_mempool.h"
#include "udk_tailq.h"
#include "udk_ethdev.h"
#include "udk_log.h"
#include "udk_cycles.h"
#include "udk_lcore.h"
#include "udk_atomic.h"
#include "udk.h"

static struct udk_mem_config early_mem_config;
static struct udk_config g_udk_config = {.process_type = UDK_PROC_PRIMARY, .mem_config = &early_mem_config};

static void udk_init_alert(const char *msg)
{
    (void)fprintf(stderr, "UDK: FATAL: %s\n", msg);
    UDK_LOG(ERR, COMMON, "%s\n", msg);
}

int udk_process_type(void)
{
    return g_udk_config.process_type;
}

uint32_t udk_page_size(void)
{
    return g_udk_config.mem_config->page_sz;
}

int udk_has_hugepages(void)
{
    return (g_udk_config.mem_config->no_hugetlbfs == 0) ? 1 : 0;
}

#define BUFF_SIZE 50
static uint32_t get_huge_page_size(void)
{
    char buf[BUFF_SIZE];
    uint32_t page_size = (uint32_t)sysconf(_SC_PAGE_SIZE);
    uint32_t size_kb;

    FILE *fp = fopen("/proc/meminfo", "r");
    if (fp == NULL) {
        goto out;
    }

    while (fgets(buf, BUFF_SIZE, fp) != NULL) {
        if ((strstr(buf, "Hugepagesize") != NULL) &&
            (sscanf_s(buf, "Hugepagesize: %u kB\n", &size_kb) > 0)) {
            page_size = size_kb * 0x400;
            break;
        }
    }

    (void)fclose(fp);

out:
    UDK_LOG(INFO, COMMON, "huge page size:%u\n", page_size);
    return page_size;
}

static int udk_config_create(void)
{
    struct udk_config *udk_cfg = udk_get_configuration();
    size_t page_sz = (size_t)sysconf(_SC_PAGE_SIZE);
    size_t cfg_len = sizeof(*udk_cfg->mem_config);
    size_t cfg_len_aligned = UDK_ALIGN(cfg_len, page_sz);
    void *mem_cfg_addr;

    mem_cfg_addr = (void *)calloc(1, cfg_len_aligned);
    if (mem_cfg_addr == NULL) {
        UDK_LOG(ERR, COMMON, "Cannot alloc memory for udk_mem_config\n");
        return -ENOMEM;
    }

    udk_cfg->mem_config = mem_cfg_addr;
    /* store address of the config in the config itself so that secondary
     * processes could later map the config into this exact location */
    udk_cfg->mem_config->mem_cfg_addr = (uint64_t)mem_cfg_addr;
    udk_cfg->mem_config->dma_maskbits = 0;
    udk_cfg->mem_config->nchannel = UDK_DEFAULT_CHANNEL_NUM;
    udk_cfg->mem_config->nrank = 1;
    udk_cfg->mem_config->page_sz = get_huge_page_size();

    return 0;
}

static void udk_config_clean(void)
{
    const struct udk_memzone *mz = NULL;
    struct udk_config *udk_cfg = udk_get_configuration();

    if (udk_cfg->mem_config != NULL) {
        mz = udk_memzone_lookup(MZ_UDK_ETH_DEV_DATA);
        if (mz != NULL) {
            (void)udk_memzone_free(mz);
        }

        udk_memzone_deinit();
        free(udk_cfg->mem_config);
        udk_cfg->mem_config = NULL;
    }

    return;
}

/* Sets up udk_config structure with the pointer to shared memory config. */
static int udk_config_init(enum udk_proc_type_t type)
{
    udk_get_configuration()->process_type = type;

    switch (type) {
        case UDK_PROC_PRIMARY:
            if (udk_config_create() < 0) {
                return -ENODEV;
            }
            break;
        case UDK_PROC_SECONDARY:
            UDK_ASSERT(0);
            break;
        case UDK_PROC_INVALID:
        default:
            UDK_LOG(ERR, COMMON, "Invalid process type %d\n", udk_process_type());
            return -EINVAL;
    }

    return 0;
}

/* After a successful call to udk_init(), there is a memory region held by
 * internal config data. So udk_deinit() must be called to release the resource
 * at a appropriate time.
 */
int udk_init(enum udk_proc_type_t type)
{
    if (udk_config_init(type) < 0) {
        udk_init_alert("Cannot init config");
        return -EINVAL;
    }

    if (udk_tailqs_init() < 0) {
        udk_init_alert("Cannot init tail queues for objects");
        goto fail_out;
    }

    if (udk_timer_init() < 0) {
        udk_init_alert("Cannot init timer");
        goto fail_out;
    }

    if (udk_lcore_init() < 0) {
        udk_init_alert("Cannot init lcore");
        goto fail_out;
    }

    if (udk_memzone_init() < 0) {
        udk_init_alert("Cannot init memzone");
        goto fail_out;
    }

    return 0;
fail_out:
    udk_deinit();
    return -EFAULT;
}

void udk_deinit(void)
{
    udk_config_clean();

    return;
}

struct udk_config *udk_get_configuration(void)
{
    return &g_udk_config;
}
